// Copyright (c) 2011-2017, XMOS Ltd, All rights reserved
#ifndef _avb_h_
#define _avb_h_
#include <xs1.h>
#include <xccompat.h>
#include "ethernet.h"
#include "otp_board_info.h"
#include "quadflashlib.h"
#include "debug_print.h"
#include "xc2compat.h"
#include "string.h"
#include "avb_1722_1_callbacks.h"
#include "gptp.h"
#include "audio_buffering.h"

/** The audio format of a 1722 Talker or Listener */
enum avb_stream_format_t
{
  AVB_FORMAT_MBLA_24BIT, /*!< 24bit MBLA */
};


/** The state of an AVB source (Talker). */
enum avb_source_state_t
{
  AVB_SOURCE_STATE_DISABLED, /*!< The source is disabled and will not transmit. */
  AVB_SOURCE_STATE_POTENTIAL, /*!< The source is enabled and will transmit if
                                a listener requests it */
  AVB_SOURCE_STATE_ENABLED, /*!< The source is enabled and transmitting */
};

/** The state of an AVB sink (Listener). */
enum avb_sink_state_t
{
  AVB_SINK_STATE_DISABLED,  /*!< The sink is disabled */
  AVB_SINK_STATE_POTENTIAL, /*!< The sink is enabled and will pass audio when a Talker requests it */
  AVB_SINK_STATE_ENABLED,   /*!< The sink is enabled and set to streaming by AEM */
};

/** The state of a media clock */
enum device_media_clock_state_t
{
  DEVICE_MEDIA_CLOCK_STATE_DISABLED, /*!< The media clock is disabled */
  DEVICE_MEDIA_CLOCK_STATE_ENABLED   /*!< The media clock is enabled */
};

/** The type of source to use as a media clock */
enum device_media_clock_type_t
{
  DEVICE_MEDIA_CLOCK_INPUT_STREAM_DERIVED, /*!< The clock is sourced from the media clock of an Input Stream */
  DEVICE_MEDIA_CLOCK_LOCAL_CLOCK           /*!< The clock is sourced from within the entity from the local crystal oscillator */
};

/** A set of media related commands generated by the AVB manager */
enum device_media_clock_commands_t
{
  DEVICE_MEDIA_CLOCK_SET_SAMPLING_RATE /*!< A command to indicate a change in nominal sampling rate */
};

typedef struct media_clock_info_t {
  int active;
  enum device_media_clock_type_t clock_type;  ///< The type of the clock
  int source;               ///< If the clock is derived from a fifo
                            ///  this is the id of the output
                            ///  fifo it should be derived from.
  int rate;                 ///<  The rate of the media clock in Hz
  int lock_counter;         ///< A count of the number of lock events on this media clock
  int unlock_counter;       ///< A count of the number of unlock events on this media clock
} media_clock_info_t;

/** Struct containing fields required for SRP reservations */
typedef struct avb_srp_info_t {
  unsigned stream_id[2];          /**< 64-bit Stream ID of the stream */
  unsigned char dest_mac_addr[6]; /**< Stream destination MAC address */
  short vlan_id;                  /**< VLAN ID for Stream */
  short tspec_max_frame_size;     /**< Maximum frame size sent by Talker */
  short tspec_max_interval;       /**< Maximum number of frames sent per class measurement interval */
  unsigned char tspec;            /**< Data Frame Priority and Rank fields */
  unsigned accumulated_latency;   /**< Latency at ingress port for Talker registrations, or latency at
                                    *  end of egress media for Listener Declarations */
  unsigned char failure_bridge_id[8]; /**< Bridge ID of bridge that changed Talker Advertise to Talker Failed */
  unsigned char failure_code;     /**< Failure code associated with the failure bridge */
} avb_srp_info_t;

typedef struct avb_stream_info_t
{
    int state;
    char tile_id;
    char local_id;
    char num_channels;
    char format;
    int rate;
    char sync;
    short flags;
} avb_stream_info_t;

typedef struct avb_source_info_t
{
    avb_srp_info_t reservation;
    avb_stream_info_t stream;
    chanend *unsafe talker_ctl;
    int presentation;
    int map[AVB_MAX_CHANNELS_PER_TALKER_STREAM];
} avb_source_info_t;


typedef struct avb_sink_info_t
{
    avb_srp_info_t reservation;
    avb_stream_info_t stream;
    chanend *unsafe listener_ctl;
    int map[AVB_MAX_CHANNELS_PER_LISTENER_STREAM];
} avb_sink_info_t;

struct avb_debug_counters {
  unsigned sent_1722;
  unsigned received_1722;
};


#ifdef __XC__
/** The core AVB interface API for interacting with the endpoint */
interface avb_interface {
  /** Intended for internal use within client interface get and set extensions only */
  avb_source_info_t _get_source_info(unsigned source_num);
  /** Intended for internal use within client interface get and set extensions only */
  void _set_source_info(unsigned source_num, avb_source_info_t info);
  /** Intended for internal use within client interface get and set extensions only */
  avb_sink_info_t _get_sink_info(unsigned sink_num);
  /** Intended for internal use within client interface get and set extensions only */
  void _set_sink_info(unsigned sink_num, avb_sink_info_t info);
  /** Intended for internal use within client interface get and set extensions only */
  media_clock_info_t _get_media_clock_info(unsigned clock_num);
  /** Intended for internal use within client interface get and set extensions only */
  void _set_media_clock_info(unsigned clock_num, media_clock_info_t info);
  /** Intended for internal use within client interface extension only */
  struct avb_debug_counters _get_debug_counters(void);
};

interface media_clock_if {
  void register_clock(unsigned i, unsigned clock_num);
  media_clock_info_t get_clock_info(unsigned clock_num);
  void set_clock_info(unsigned clock_num, media_clock_info_t info);
  void set_buf_fifo(unsigned i, int fifo);
};


extends client interface avb_interface : {
  /** Get the format of an AVB source.
   *  \param i          interface to AVB manager
   *  \param source_num the local source number
   *  \param format     the format of the stream
   *  \param rate       the sample rate of the stream in Hz
   */
  static inline int get_source_format(client interface avb_interface i, unsigned source_num,
                        enum avb_stream_format_t &format,
                        int &rate)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    format = source.stream.format;
    rate = source.stream.rate;
    return 1;
  }

  /** Set the format of an AVB source.
   *
   *  The AVB source format covers the encoding and sample rate of the source.
   *  Currently the format is limited to a single encoding MBLA 24 bit signed
   *  integers.
   *
   *  This setting will not take effect until the next time the source
   *  state moves from disabled to potential.
   *
   *  \param i          interface to AVB manager
   *  \param source_num the local source number
   *  \param format     the format of the stream
   *  \param rate       the sample rate of the stream in Hz
   */
  static inline int set_source_format(client interface avb_interface i, unsigned source_num,
                        enum avb_stream_format_t format, int rate)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    if (source.stream.state != AVB_SOURCE_STATE_DISABLED)
      return 0;
    source.stream.format = format;
    source.stream.rate = rate;
    i._set_source_info(source_num, source);
    return 1;
  }

  /** Get the channel count of an AVB source.
   *  \param i          interface to AVB manager
   *  \param source_num   the local source number
   *  \param channels     the number of channels
   */
  static inline int get_source_channels(client interface avb_interface i, unsigned source_num,
                          int &channels)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    channels = source.stream.num_channels;
    return 1;
  }

  /** Set the channel count of an AVB source.
   *
   *  Sets the number of channels in the stream.
   *
   *  This setting will not take effect until the next time the source
   *  state moves from disabled to potential.
   *
   *  \param i            interface to AVB manager
   *  \param source_num   the local source number
   *  \param channels     the number of channels
   */
  static inline int set_source_channels(client interface avb_interface i, unsigned source_num,
                          int channels)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    if (source.stream.state != AVB_SOURCE_STATE_DISABLED)
      return 0;
    source.stream.num_channels = channels;
    i._set_source_info(source_num, source);
    return 1;
  }

  /** Get the media clock of an AVB source.
   *  \param i            interface to AVB manager
   *  \param source_num   the local source number
   *  \param sync         the media clock number
   */
  static inline int get_source_sync(client interface avb_interface i, unsigned source_num,
                      int &sync)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    sync = source.stream.sync;
    return 1;
  }

  /** Set the media clock of an AVB source.
   *
   *  Sets the media clock of the stream.
   *
   *  \param i            interface to AVB manager
   *  \param source_num   the local source number
   *  \param sync         the media clock number
   */
  static inline int set_source_sync(client interface avb_interface i, unsigned source_num,
                      int sync)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    if (source.stream.state != AVB_SOURCE_STATE_DISABLED)
      return 0;
    source.stream.sync = sync;
    i._set_source_info(source_num, source);
    return 1;
  }

  /** Get the presentation time offset of an AVB source.
   *  \param i            interface to AVB manager
   *  \param source_num       the local source number to set
   *  \param presentation     the presentation offset in ms
   */
  static inline int get_source_presentation(client interface avb_interface i, unsigned source_num,
                              int &presentation)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    presentation = source.presentation;
    return 1;
  }

  /** Set the presentation time offset of an AVB source.
   *
   *  Sets the presentation time offset of a source i.e. the
   *  time after sampling that the stream should be played. The default
   *  value for this is 2ms.
   *
   *  This setting will not take effect until the next time the source
   *  state moves from disabled to potential.
   *
   *  \param i                interface to AVB manager
   *  \param source_num       the local source number to set
   *  \param presentation     the presentation offset in ms
   *
   *
   **/
  static inline int set_source_presentation(client interface avb_interface i, unsigned source_num,
                              int presentation)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    if (source.stream.state != AVB_SOURCE_STATE_DISABLED)
      return 0;
    source.presentation = presentation;
    i._set_source_info(source_num, source);
    return 1;
  }


  /** Get the destination vlan of an AVB source.
   *  \param i          interface to AVB manager
   *  \param source_num the local source number
   *  \param vlan       the destination vlan id
   */
  static inline int get_source_vlan(client interface avb_interface i, unsigned source_num,
                      int &vlan)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    vlan = source.reservation.vlan_id;
    return 1;
  }

  /** Set the destination vlan of an AVB source.
   *
   *  Sets the vlan that the source will transmit on. This defaults
   *  to 2.
   *
   *  This setting will not take effect until the next time the source
   *  state moves from disabled to potential.
   *
   *  \param i          interface to AVB manager
   *  \param source_num the local source number
   *  \param vlan       the destination vlan id
   */
  static inline int set_source_vlan(client interface avb_interface i, unsigned source_num,
                      int vlan)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    source.reservation.vlan_id = vlan;
    i._set_source_info(source_num, source);
    return 1;
  }

  /** Get the current state of an AVB source.
   *  \param i          interface to AVB manager
   *  \param source_num the local source number
   *  \param state      the state of the source
   */
  static inline int get_source_state(client interface avb_interface i, unsigned source_num,
                       enum avb_source_state_t &state)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    state = source.stream.state;
    return 1;
  }

  /** Set the current state of an AVB source.
   *
   *  Sets the current state of an AVB source. You cannot set the
   *  state to ``ENABLED``. Changing the state to ``AVB_SOURCE_STATE_POTENTIAL`` turns the stream
   *  on and SRP will automatically set it to ``ENABLED`` when connected to
   *  a listener and streaming.
   *
   *  \param i          interface to AVB manager
   *  \param source_num the local source number
   *  \param state      the state of the source
   */
  static inline int set_source_state(client interface avb_interface i, unsigned source_num,
                       enum avb_source_state_t state)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    source.stream.state = state;
    i._set_source_info(source_num, source);
    return 1;
  }

  /** Get the channel map of an avb source.
   *  \param i   interface to AVB manager
   *  \param source_num the local source number to set
   *  \param map the map, an array of integers giving the input FIFOs that
   *             make up the stream
   *  \param len the length of the map; should be equal to the number of channels
   *             in the stream
   */
  static inline int get_source_map(client interface avb_interface i, unsigned source_num,
                     int map[], int &len)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    len = source.stream.num_channels;
    memcpy(map, source.map, len<<2);
    return 1;
  }

  /** Set the channel map of an avb source.
   *
   *  Sets the channel map of a source i.e. the list of
   *  input FIFOs that constitute the stream.
   *
   *  This setting will not take effect until the next time the source
   *  state moves from disabled to potential.
   *
   *  \param i   interface to AVB manager
   *  \param source_num the local source number to set
   *  \param map the map, an array of integers giving the input FIFOs that
   *             make up the stream
   *  \param len the length of the map; should be equal to the number of channels
   *             in the stream
   *
   **/
  static inline int set_source_map(client interface avb_interface i, unsigned source_num,
                     int map[len], unsigned len)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    if (source.stream.state != AVB_SOURCE_STATE_DISABLED)
      return 0;
    if (len > AVB_MAX_CHANNELS_PER_TALKER_STREAM)
      return 0;
    memcpy(source.map, map, len<<2);
    i._set_source_info(source_num, source);
    return 1;
  }

  /** Get the destination address of an avb source.
   *  \param i            interface to AVB manager
   *  \param source_num   the local source number
   *  \param addr         the destination address as an array of 6 bytes
   *  \param len          the length of the address, should always be equal to 6
   */
  static inline int get_source_dest(client interface avb_interface i, unsigned source_num,
                      unsigned char addr[], int &len)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    len = 6;
    memcpy(addr, source.reservation.dest_mac_addr, 6);
    return 1;
  }

  /** Set the destination address of an avb source.
   *
   *  Sets the destination MAC address of a source.
   *  This setting will not take effect until the next time the source
   *  state moves from disabled to potential.
   *
   *  \param i            interface to AVB manager
   *  \param source_num   the local source number
   *  \param addr         the destination address as an array of 6 bytes
   *  \param len          the length of the address, should always be equal to 6
   *
   **/
  static inline int set_source_dest(client interface avb_interface i, unsigned source_num,
                      unsigned char addr[len], unsigned len)
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    if (source.stream.state != AVB_SOURCE_STATE_DISABLED)
      return 0;
    if (len != 6)
      return 0;
    memcpy(source.reservation.dest_mac_addr, addr, 6);
    i._set_source_info(source_num, source);
    return 1;
  }

  static inline int get_source_id(client interface avb_interface i, unsigned source_num,
                    unsigned int id[2])
  {
    if (source_num >= AVB_NUM_SOURCES)
      return 0;
    avb_source_info_t source;
    source = i._get_source_info(source_num);
    memcpy(id, source.reservation.stream_id, 8);
    return 1;
  }

  /** Get the stream id that an AVB sink listens to.
   * \param i            interface to AVB manager
   * \param sink_num      the number of the sink
   * \param stream_id     int array containing the 64-bit of the stream
   */
  static inline int get_sink_id(client interface avb_interface i, unsigned sink_num,
                  unsigned int stream_id[2])
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    memcpy(stream_id, sink.reservation.stream_id, 8);
    return 1;
  }

  /** Set the stream id that an AVB sink listens to.
   *
   *  Sets the stream id that an AVB sink listens to.
   *
   *  This setting will not take effect until the next time the sink
   *  state moves from disabled to potential.
   *
   * \param i             interface to AVB manager
   * \param sink_num      the number of the sink
   * \param stream_id     int array containing the 64-bit of the stream
   *
   */
  static inline int set_sink_id(client interface avb_interface i, unsigned sink_num,
                  unsigned int stream_id[2])
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (sink.stream.state != AVB_SINK_STATE_DISABLED)
      return 0;
    memcpy(sink.reservation.stream_id, stream_id, 8);
    i._set_sink_info(sink_num, sink);
    return 1;
  }


   /** Get the format of an AVB sink.
   *  \param i          interface to AVB manager
   *  \param sink_num the local sink number
   *  \param format     the format of the stream
   *  \param rate       the sample rate of the stream in Hz
   */
  static inline int get_sink_format(client interface avb_interface i, unsigned sink_num,
                      enum avb_stream_format_t &format, int &rate)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    format = sink.stream.format;
    rate = sink.stream.rate;
    return 1;
  }

  /** Set the format of an AVB sink.
   *
   *  The AVB sink format covers the encoding and sample rate of the sink.
   *  Currently the format is limited to a single encoding MBLA 24 bit signed
   *  integers.
   *
   *  This setting will not take effect until the next time the sink
   *  state moves from disabled to potential.
   *
   *  \param i            interface to AVB manager
   *  \param sink_num     the local sink number
   *  \param format       the format of the stream
   *  \param rate         the sample rate of the stream in Hz
   */
  static inline int set_sink_format(client interface avb_interface i, unsigned sink_num,
                      enum avb_stream_format_t format, int rate)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (sink.stream.state != AVB_SINK_STATE_DISABLED)
      return 0;
    sink.stream.format = format;
    sink.stream.rate = rate;
    i._set_sink_info(sink_num, sink);
    return 1;
  }

  /** Get the channel count of an AVB sink.
   *  \param i            interface to AVB manager
   *  \param sink_num     the local sink number
   *  \param channels     the number of channels
   */
  static inline int get_sink_channels(client interface avb_interface i, unsigned sink_num,
                        int &channels)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    channels = sink.stream.num_channels;
    return 1;
  }

  /** Set the channel count of an AVB sink.
   *
   *  Sets the number of channels in the stream.
   *
   *  This setting will not take effect until the next time the sink
   *  state moves from disabled to potential.
   *
   *  \param i            interface to AVB manager
   *  \param sink_num     the local sink number
   *  \param channels     the number of channels
   */
  static inline int set_sink_channels(client interface avb_interface i, unsigned sink_num,
                        int channels)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (sink.stream.state != AVB_SINK_STATE_DISABLED)
      return 0;
    sink.stream.num_channels = channels;
    i._set_sink_info(sink_num, sink);
    return 1;
  }

  /** Get the media clock of an AVB sink.
   *  \param i            interface to AVB manager
   *  \param sink_num     the local sink number
   *  \param sync         the media clock number
   */
  static inline int get_sink_sync(client interface avb_interface i, unsigned sink_num,
                    int &sync)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    sync = sink.stream.sync;
    return 1;
  }

  /** Set the media clock of an AVB sink.
   *
   *  Sets the media clock of the stream.
   *
   *  \param i            interface to AVB manager
   *  \param sink_num     the local sink number
   *  \param sync         the media clock number
   */
  static inline int set_sink_sync(client interface avb_interface i, unsigned sink_num,
                    int sync)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (sink.stream.state != AVB_SINK_STATE_DISABLED)
      return 0;
    sink.stream.sync = sync;
    i._set_sink_info(sink_num, sink);
    return 1;
  }

  /** Get the virtual lan id of an AVB sink.
   * \param i        interface to AVB manager
   * \param sink_num the number of the sink
   * \param vlan     the vlan id of the sink
   */
  static inline int get_sink_vlan(client interface avb_interface i, unsigned sink_num,
                    int &vlan)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    vlan = sink.reservation.vlan_id;
    return 1;
  }

  /** Set the virtual lan id of an AVB sink.
   *
   *  Sets the vlan id of the incoming stream.
   *
   *  This setting will not take effect until the next time the sink
   *  state moves from disabled to potential.
   *
   * \param i        interface to AVB manager
   * \param sink_num the number of the sink
   * \param vlan     the vlan id of the sink
   *
   */
  static inline int set_sink_vlan(client interface avb_interface i, unsigned sink_num,
                                  int vlan)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (sink.stream.state != AVB_SINK_STATE_DISABLED)
      return 0;
    sink.reservation.vlan_id = vlan;
    i._set_sink_info(sink_num, sink);
    return 1;
  }

  /** Get the incoming destination mac address of an avb sink.
   *  \param i            Interface to AVB manager
   *  \param sink_num     The local sink number
   *  \param addr         The mac address as an array of 6 bytes.
   *  \param len          The length of the address, should always be equal to 6.
   */
  static inline int get_sink_addr(client interface avb_interface i, unsigned sink_num,
                    unsigned char addr[], int &len)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    len = 6;
    memcpy(addr, sink.reservation.dest_mac_addr, 6);
    return 1;
  }

  /** Set the incoming destination mac address of an avb sink.
   *
   *  Set the incoming destination mac address of a sink.
   *  This needs to be set if the address is a multicast address so
   *  the endpoint can register for that multicast group with the switch.
   *
   *  This setting will not take effect until the next time the sink
   *  state moves from disabled to potential.
   *
   *  \param i            Interface to AVB manager
   *  \param sink_num     The local sink number
   *  \param addr         The mac address as an array of 6 bytes.
   *  \param len          The length of the address, should always be equal to 6.
   *
   **/
  static inline int set_sink_addr(client interface avb_interface i, unsigned sink_num,
                    unsigned char addr[len], unsigned len)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (sink.stream.state != AVB_SINK_STATE_DISABLED)
      return 0;
    if (len != 6)
      return 0;
    memcpy(sink.reservation.dest_mac_addr, addr, 6);
    i._set_sink_info(sink_num, sink);
    return 1;
  }

  /** Get the state of an AVB sink.
   * \param i     interface to AVB manager
   * \param sink_num the number of the sink
   * \param state the state of the sink
   */
  static inline int get_sink_state(client interface avb_interface i, unsigned sink_num,
                     enum avb_sink_state_t &state)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    state = sink.stream.state;
    return 1;
  }

  /** Set the state of an AVB sink.
   *
   *  Sets the current state of an AVB sink. You cannot set the
   *  state to ``ENABLED``. Changing the state to ``POTENTIAL`` turns the stream
   *  on and it will automatically pass audio once audio is available.
   *
   * \param i     interface to AVB manager
   * \param sink_num the number of the sink
   * \param state the state of the sink
   *
   */
  static inline int set_sink_state(client interface avb_interface i, unsigned sink_num,
                     enum avb_sink_state_t state)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    sink.stream.state = state;
    i._set_sink_info(sink_num, sink);
    return 1;
  }

  /** Get the map of an AVB sink.
   * \param i          interface to AVB manager
   * \param sink_num   the number of the sink
   * \param map        array containing the media output FIFOs that the
   *                   stream will be split into
   * \param len        the length of the map; should equal to the number
   *                   of channels in the stream
   */
  static inline int get_sink_map(client interface avb_interface i, unsigned sink_num,
                   int map[], int &len)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    len = sink.stream.num_channels;
    memcpy(map, sink.map, len<<2);
    return 1;
  }

  /** Set the map of an AVB sink.
   *
   *  Sets the map i.e. the mapping from the 1722 stream to output FIFOs.
   *
   *  This setting will take affect immediately.
   *
   * \param i          interface to AVB manager
   * \param sink_num   the number of the sink
   * \param map        array containing the media output FIFOs that the
   *                   stream will be split into
   * \param len        the length of the map; should equal to the number
   *                   of channels in the stream
   */
  static inline int set_sink_map(client interface avb_interface i, unsigned sink_num,
                   int map[len], unsigned len)
  {
    if (sink_num >= AVB_NUM_SINKS)
      return 0;
    avb_sink_info_t sink;
    sink = i._get_sink_info(sink_num);
    if (len > AVB_MAX_CHANNELS_PER_LISTENER_STREAM)
      return 0;
    memcpy(sink.map, map, len<<2);
    i._set_sink_info(sink_num, sink);
    return 1;
  }


  /** Get the rate of a media clock.
   *  \param i    interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param rate the rate of the clock in Hz
   */
  static inline int get_device_media_clock_rate(client interface avb_interface i,
                                  int clock_num, int &rate)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    rate = info.rate;
    return 1;
  }

  /** Set the rate of a media clock.
   *
   *  Sets the rate of the media clock.
   *
   *  \param i    interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param rate the rate of the clock in Hz
   *
   **/
  static inline int set_device_media_clock_rate(client interface avb_interface i,
                                  int clock_num, int rate)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    info.rate = rate;
    i._set_media_clock_info(clock_num, info);
    return 1;
  }

  /** Get the state of a media clock.
   *  \param i     interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param state the state of the clock
   */
  static inline int get_device_media_clock_state(client interface avb_interface i,
                                   int clock_num,
                                   enum device_media_clock_state_t &state)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    state = info.active ? DEVICE_MEDIA_CLOCK_STATE_ENABLED :
                          DEVICE_MEDIA_CLOCK_STATE_DISABLED;
    return 1;
  }

  /** Set the state of a media clock.
   *
   *  This function can be used to enabled/disable a media clock.
   *
   *  \param i     interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param state the state of the clock
   **/
  static inline int set_device_media_clock_state(client interface avb_interface i,
                                   int clock_num,
                                   enum device_media_clock_state_t state)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    info.active = (state == DEVICE_MEDIA_CLOCK_STATE_ENABLED);
    i._set_media_clock_info(clock_num, info);
    return 1;
  }

  /** Get the source of a media clock.
   *  \param i      interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param source the output FIFO number to base the clock on
   */
  static inline int get_device_media_clock_source(client interface avb_interface i,
                                    int clock_num, int &source)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    source = info.source;
    return 1;
  }

  /** Set the source of a media clock.
   *
   *  For clocks that are derived from an output FIFO. This function
   *  gets/sets which FIFO the clock should be derived from.
   *
   *  \param i      interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param source the output FIFO number to base the clock on
   *
   **/
  static inline int set_device_media_clock_source(client interface avb_interface i,
                                    int clock_num, int source)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    info.source = source;
    i._set_media_clock_info(clock_num, info);
    return 1;
  }


  /** Get the type of a media clock.
   *
   *  \param i          interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param clock_type the type of the clock
   */
  static inline int get_device_media_clock_type(client interface avb_interface i,
                                  int clock_num,
                                  enum device_media_clock_type_t &clock_type)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    clock_type = info.clock_type;
    return 1;
  }

  /** Set the type of a media clock.
   *
   *  \param i          interface to AVB manager
   *  \param clock_num the number of the media clock
   *  \param clock_type the type of the clock
   *
   **/
  static inline int set_device_media_clock_type(client interface avb_interface i,
                                  int clock_num,
                                  enum device_media_clock_type_t clock_type)
  {
    if (clock_num >= AVB_NUM_MEDIA_CLOCKS)
      return 0;
    media_clock_info_t info;
    info = i._get_media_clock_info(clock_num);
    info.clock_type = clock_type;
    debug_printf("Setting clock source:");
    if (info.clock_type) debug_printf(" LOCAL_CLOCK\n");
    else debug_printf(" INPUT_STREAM_DERIVED\n");
    i._set_media_clock_info(clock_num, info);
    return 1;
  }

  /** Read back debug counters
    *
    * \param i          interface to AVB manager
    * \return structure of counters passed by value
    *
    **/
  static inline struct avb_debug_counters get_debug_counters(client interface avb_interface i)
  {
    return i._get_debug_counters();
  }
}

/** An interface used to register and deregister stream reservations via MSRP */
interface srp_interface {
  /** Used by a Talker application entity to issue a request to the MSRP Participant
   *  to initiate the advertisement of an available Stream
   *
   *  \param stream_info Struct of type avb_srp_info_t containing parameters of the stream to register
   */
  short register_stream_request(avb_srp_info_t stream_info);

  /** Used by a Talker application entity to request removal of the Talker’s advertisement declaration,
   *  and thus remove the advertisement of a Stream, from the network.
   *
   *  \param stream_id two int array containing the Stream ID of the stream to deregister
   */
  void deregister_stream_request(unsigned stream_id[2]);

  /** Used by a Listener application entity to issue a request to attach to the referenced Stream.
   *
   *  \param stream_id two int array containing the Stream ID of the stream to register
   *  \param vlan_id   the VLAN ID associated with the stream. If 0 the current VID from the SRP domain will be used.
   */
  short register_attach_request(unsigned stream_id[2], short vlan_id);

  /** Used by a Listener application entity to remove the request to attach to the referenced Stream.
   *
   *  \param stream_id two int array containing the Stream ID of the stream to deregister
   */
  void deregister_attach_request(unsigned stream_id[2]);
};


/** Core AVB API management task that can be combined with other AVB tasks such as SRP or 1722.1

 * \param i_avb[]           array of avb_interface server interfaces connected to clients of avb_manager
 * \param num_avb_clients   number of client interface connections to the server and the number of elements of i_avb[]
 * \param i_srp            client interface of type srp_interface into an srp_task() task
 * \param c_media_ctl[]     array of chanends connected to components that register/control media FIFOs
 * \param c_listener_ctl[]  array of chanends connected to components that register/control IEEE 1722 sinks
 * \param c_talker_ctl[]    array of chanends connected to components that register/control IEEE 1722 sources
 * \param i_eth_cfg         a client interface of type ethernet_cfg_if for Ethernet MAC configuration
 * \param i_media_clock_ctl client interface of type media_clock_if connected to the media clock server
 */
[[combinable]]
void avb_manager(server interface avb_interface i_avb[num_avb_clients], unsigned num_avb_clients,
                 client interface srp_interface ?i_srp,
                 chanend c_media_ctl[],
                 chanend (&?c_listener_ctl)[],
                 chanend (&?c_talker_ctl)[],
                 client interface ethernet_cfg_if i_eth_cfg,
                 client interface media_clock_if ?i_media_clock_ctl);


/** A task that runs MAAP and 1722.1 ADP, ACMP and AECP protocols and interacts with the rest of the AVB stack.
  *
  *  Can be combined with other combinable tasks.
  *
  *  \param  otp_ports    reference to an OTP ports structure of type otp_ports_t
  *  \param  i_avb   client interface of type avb_interface into avb_manager()
  *  \param  i_1722_1_entity client interface of type avb_1722_1_control_callbacks
  *  \param  qspi_ports  a reference to a Quad SPI flash ports structure
  *  \param  i_eth_rx a client receive interface into the Ethernet MAC
  *  \param  i_eth_tx a client transmit interface into the Ethernet MAC
  *  \param  i_eth_cfg a client interface for Ethernet MAC configuration
  *  \param  c_ptp chanend into the PTP server
  */
[[combinable]]
void avb_1722_1_maap_task(otp_ports_t &?otp_ports,
                         client interface avb_interface i_avb,
                         client interface avb_1722_1_control_callbacks i_1722_1_entity,
                         fl_QSPIPorts &?qspi_ports,
                         client interface ethernet_rx_if i_eth_rx,
                         client interface ethernet_tx_if i_eth_tx,
                         client interface ethernet_cfg_if i_eth_cfg,
                         chanend c_ptp);

/** A task that runs SRP, MAAP and 1722.1 ADP, ACMP and AECP protocols and interacts with the rest of the AVB stack.
  *
  *  Can be combined with other combinable tasks.
  *
  *  \param  otp_ports    reference to an OTP ports structure of type otp_ports_t
  *  \param  i_avb   client interface of type avb_interface into avb_manager()
  *  \param  i_1722_1_entity client interface of type avb_1722_1_control_callbacks
  *  \param  qspi_ports  a reference to a Quad SPI flash ports structure
  *  \param  i_eth_rx a client receive interface into the Ethernet MAC
  *  \param  i_eth_tx a client transmit interface into the Ethernet MAC
  *  \param  i_eth_cfg a client interface for Ethernet MAC configuration
  *  \param  c_ptp chanend into the PTP server
  */
[[combinable]]
void avb_1722_1_maap_srp_task(client interface avb_interface i_avb,
                              client interface avb_1722_1_control_callbacks i_1722_1_entity,
                              fl_QSPIPorts &?qspi_ports,
                              client interface ethernet_rx_if i_eth_rx,
                              client interface ethernet_tx_if i_eth_tx,
                              client interface ethernet_cfg_if i_eth_cfg,
                              chanend c_ptp,
                              otp_ports_t &?otp_ports);

/** SRP task that implements MSRP and MVRP protocols. Can be combined with other combinable tasks.
  *
  * \param i_avb  client interface of type avb_interface into the avb_manager()
                  for API control of the stack
    \param i_srp server interface of type srp_interface that offers client tasks
                  access to SRP reservation functionality
  *  \param  i_eth_rx a client receive interface into the Ethernet MAC
  *  \param  i_eth_tx a client transmit interface into the Ethernet MAC
  *  \param  i_eth_cfg a client interface for Ethernet MAC configuration
  */
[[combinable]]
void avb_srp_task(client interface avb_interface i_avb,
                  server interface srp_interface i_srp,
                  client interface ethernet_rx_if i_eth_rx,
                  client interface ethernet_tx_if i_eth_tx,
                  client interface ethernet_cfg_if i_eth_cfg);

/** An AVB IEEE 1722 audio talker thread.
 *  This thread implements a talker, taking
 *  media input FIFOs and combining them into 1722 packets to be
 *  sent to the ethernet component. It is dynamically configured
 *  using the AVB control API.
 *
 *  \param c_ptp            link to the PTP timing server
 *  \param c_eth_tx_hp      a high priority client transmit interface into the Ethernet MAC
 *  \param c_talker_ctl     channel to configure the talker
 *  \param num_streams      the number of streams the unit controls
 *  \param audio_input_buf  a client interface to get a handle to pull from the audio input buffer
 **/
void avb_1722_talker(chanend c_ptp,
                     streaming chanend c_eth_tx_hp,
                     chanend c_talker_ctl,
                     int num_streams,
                     client pull_if audio_input_buf);

/** An AVB IEEE 1722 audio listener thread.
 *
 *  This thread implements a listener. It takes IEEE 1722 packets from
 *  the ethernet MAC and splits them into output FIFOs. The
 *  buffer fill level of these streams is set in conjunction with communication
 *  to the media clock server. This thread is dynamically configured
 *  using the AVB control API.
 *
 *  \param c_eth_rx_hp      a high priority client receive interface into the Ethernet MAC
 *  \param c_buf_ctl        buffer control link to the media clock server
 *  \param c_ptp_ctl        PTP server link for retrieving PTP time info
 *  \param c_listener_ctl   channel to configure the listener (given
 *                          to avb_init())
 *  \param num_streams      the number of streams the unit will handle
 *  \param audio_output_buf a client interface to get a handle to push to the audio output buffer
 */
void avb_1722_listener(streaming chanend c_eth_rx_hp,
                       chanend c_buf_ctl,
                       chanend? c_ptp_ctl,
                       chanend c_listener_ctl,
                       int num_streams,
                       client push_if audio_output_buf);

/** The media clock server.
 *
 *  \param media_clock_ctl  server interface of type media_clock_if connected to the avb_manager() task
 *  \param ptp_svr          chanend connected to the PTP server
 *  \param buf_ctl[]        array of links to listener components
 *                          requiring buffer management
 *  \param num_buf_ctl      size of the buf_ctl array
 *  \param p_fs             output port to drive PLL reference clock
 *  \param i_eth_rx         a client receive interface into the Ethernet MAC
 *  \param i_eth_tx         a client transmit interface into the Ethernet MAC
 *  \param i_eth_cfg        a client interface for Ethernet MAC configuration
 *  \param c_ptp[]          an array of chanends to connect to clients of the ptp server
 *  \param num_ptp          The number of PTP clients attached
 *  \param server_type      The type of the PTP server (``PTP_GRANDMASTER_CAPABLE``
                            or ``PTP_SLAVE_ONLY``)
 */
void gptp_media_clock_server(server interface media_clock_if media_clock_ctl,
                            chanend ?ptp_svr,
                            chanend buf_ctl[num_buf_ctl], unsigned num_buf_ctl,
                            out buffered port:32 p_fs[],
                            client interface ethernet_rx_if i_eth_rx,
                            client interface ethernet_tx_if i_eth_tx,
                            client interface ethernet_cfg_if i_eth_cfg,
                            chanend c_ptp[num_ptp],
                            unsigned num_ptp,
                            enum ptp_server_type server_type
                            );

#endif

int avb_get_source_state(CLIENT_INTERFACE(avb_interface, avb), unsigned source_num, REFERENCE_PARAM(enum avb_source_state_t, state));
int avb_set_source_state(CLIENT_INTERFACE(avb_interface, avb), unsigned source_num, enum avb_source_state_t state);
int avb_get_source_vlan(CLIENT_INTERFACE(avb_interface, avb), unsigned source_num, REFERENCE_PARAM(int, vlan));
int avb_set_source_vlan(CLIENT_INTERFACE(avb_interface, avb), unsigned source_num, int vlan);
int avb_get_sink_vlan(CLIENT_INTERFACE(avb_interface, avb), unsigned sink_num, REFERENCE_PARAM(int, vlan));
int avb_set_sink_vlan(CLIENT_INTERFACE(avb_interface, avb), unsigned sink_num, int vlan);

#endif // _avb_h_
